package com.atguigu.daijia.map.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.atguigu.daijia.common.constant.RedisConstant;
import com.atguigu.daijia.common.result.Result;
import com.atguigu.daijia.driver.client.DriverInfoFeignClient;
import com.atguigu.daijia.map.service.LocationService;
import com.atguigu.daijia.model.entity.driver.DriverSet;
import com.atguigu.daijia.model.entity.map.OrderServiceLocation;
import com.atguigu.daijia.model.form.map.OrderServiceLocationForm;
import com.atguigu.daijia.model.form.map.SearchNearByDriverForm;
import com.atguigu.daijia.model.form.map.UpdateDriverLocationForm;
import com.atguigu.daijia.model.form.map.UpdateOrderLocationForm;
import com.atguigu.daijia.model.vo.map.NearByDriverVo;
import com.atguigu.daijia.model.vo.map.OrderLocationVo;
import com.atguigu.daijia.model.vo.map.OrderServiceLastLocationVo;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.bson.types.ObjectId;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.cache.CacheProperties;
import org.springframework.data.domain.Sort;
import org.springframework.data.geo.*;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.BasicQuery;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.domain.geo.GeoReference;
import org.springframework.data.redis.domain.geo.Metrics;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;

@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class LocationServiceImpl implements LocationService {
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private MongoTemplate mongoTemplate;


    @Resource
    private DriverInfoFeignClient driverInfoFeignClient;


    @Override
    public Boolean updateDriverLocation(UpdateDriverLocationForm updateDriverLocationForm) {
        Point point = new Point(updateDriverLocationForm.getLongitude().doubleValue(),
                updateDriverLocationForm.getLatitude().doubleValue());
        Long add = stringRedisTemplate.opsForGeo()
                .add(RedisConstant.DRIVER_GEO_LOCATION, point, updateDriverLocationForm.getDriverId().toString());
        return true;
    }

    @Override
    public Boolean removeDriverLocation(Long driverId) {
        Long remove = stringRedisTemplate.opsForGeo().remove(RedisConstant.DRIVER_GEO_LOCATION, driverId.toString());
        return remove > 0;
    }

    @Override
    public List<NearByDriverVo> searchNearByDriver(SearchNearByDriverForm searchNearByDriverForm) {
        ArrayList<NearByDriverVo> nearByDriverVoArrayList = new ArrayList<>();
        // 1. 先获取自身位置
        Point center = new Point(searchNearByDriverForm.getLongitude().doubleValue(),
                searchNearByDriverForm.getLatitude().doubleValue());
        // 2. 周围5公里以内的司机
        Distance distance = new Distance(5000, Metrics.METERS);
        RedisGeoCommands.GeoRadiusCommandArgs args =
                RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs()
                        .includeDistance()  // 包含距离
                        .includeCoordinates() // 包含坐标
                        .sortAscending(); // 升序
        // 3. 获取司机列表
        GeoResults<RedisGeoCommands.GeoLocation<String>> geoResults = stringRedisTemplate.opsForGeo().radius(RedisConstant.DRIVER_GEO_LOCATION, new Circle(center, distance), args);
        if (geoResults == null) {
            return nearByDriverVoArrayList;
        }
        List<GeoResult<RedisGeoCommands.GeoLocation<String>>> geoResultList = geoResults.getContent();
        if (CollectionUtils.isEmpty(geoResultList)) {
            return nearByDriverVoArrayList;
        }
        // 4. 遍历 司机的geo信息列表
        geoResultList.stream().forEach(geoResult -> {
            RedisGeoCommands.GeoLocation<String> geoLocationGeoResultContent = geoResult.getContent();
            // 4.1 获取司机id、司机与乘客的距离
            String driverId = geoLocationGeoResultContent.getName();
            Double metersDriver2Customer = geoResult.getDistance().getValue();
            // 4.2 远程调用，查看司机的个性化信息
            Result<DriverSet> result = driverInfoFeignClient.getDriverSet(Long.parseLong(driverId));
            DriverSet driverSet = result.getData();
            // 4.3 判断接单里程、乘客与司机之间的距离
            BigDecimal orderDistance = driverSet.getOrderDistance();
            BigDecimal acceptDistance = driverSet.getAcceptDistance();
            if (orderDistance.compareTo(searchNearByDriverForm.getMileageDistance()) > 0 && acceptDistance.compareTo(new BigDecimal(metersDriver2Customer)) > 0) {
                // 符合标准的 放入 司机列表
                NearByDriverVo nearByDriverVo = new NearByDriverVo();
                nearByDriverVo.setDriverId(Long.parseLong(driverId));
                nearByDriverVo.setDistance(new BigDecimal(metersDriver2Customer));
                nearByDriverVoArrayList.add(nearByDriverVo);
            }
        });
        // 5. 返回结果
        return nearByDriverVoArrayList;
    }

    // 司机赶往代驾起始点：更新订单地址到缓存
    @Override
    public Boolean updateOrderLocationToCache(UpdateOrderLocationForm updateOrderLocationForm) {

        OrderLocationVo orderLocationVo = new OrderLocationVo();
        orderLocationVo.setLongitude(updateOrderLocationForm.getLongitude());
        orderLocationVo.setLatitude(updateOrderLocationForm.getLatitude());

        String key = RedisConstant.UPDATE_ORDER_LOCATION + updateOrderLocationForm.getOrderId();
        String jsonString = JSONObject.toJSONString(orderLocationVo);
        stringRedisTemplate.opsForValue().set(key, jsonString);
        return true;
    }

    @Override
    public OrderLocationVo getCacheOrderLocation(Long orderId) {
        String key = RedisConstant.UPDATE_ORDER_LOCATION + orderId;
        String jsonString = stringRedisTemplate.opsForValue().get(key);
        OrderLocationVo orderLocationVo = JSONObject.toJavaObject((JSONObject) JSONObject.parse(jsonString), OrderLocationVo.class);
        return orderLocationVo;
    }

    // 批量保存代驾服务订单位置
    @Override
    public Boolean saveOrderServiceLocation(List<OrderServiceLocationForm> orderLocationServiceFormList) {
        ArrayList<OrderServiceLocation> list = new ArrayList<>();
        orderLocationServiceFormList.forEach(orderServiceLocationForm -> {
            OrderServiceLocation orderServiceLocation = new OrderServiceLocation();
            BeanUtils.copyProperties(orderServiceLocationForm, orderServiceLocation);
            orderServiceLocation.setCreateTime(new Date());
            orderServiceLocation.setId(ObjectId.get().toString());
            list.add(orderServiceLocation);
        });
        mongoTemplate.insertAll(list);
        return true;
    }

    @Override
    public OrderServiceLastLocationVo getOrderServiceLastLocation(Long orderId) {
        Query query = new Query();
        query.addCriteria(Criteria.where("orderId").is(orderId));
        query.with(Sort.by(Sort.Order.by("createTime")));
        query.limit(1);

        OrderServiceLocation orderServiceLocation = mongoTemplate.findOne(query, OrderServiceLocation.class);
        OrderServiceLastLocationVo orderServiceLastLocationVo = new OrderServiceLastLocationVo();
        BeanUtils.copyProperties(orderServiceLocation, orderServiceLastLocationVo);
        return orderServiceLastLocationVo;
    }
}
